{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "2bbdcf85-3392-4ba4-aeba-df4a184ebdde",
   "metadata": {},
   "outputs": [],
   "source": [
    "import cv2\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from pathlib import Path\n",
    "from skimage.feature import graycomatrix, graycoprops\n",
    "from pywt import dwt2\n",
    "from scipy.fftpack import dct, fft2, fftshift"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "58f38481-c24d-4bd0-b329-e20db918fd78",
   "metadata": {},
   "outputs": [],
   "source": [
    "class MultiStreamForgeryDetection:\n",
    "    def __init__(self, image_path):\n",
    "        # 初始化输入和路径\n",
    "        self.image_path = image_path\n",
    "        self.image = cv2.imread(image_path)\n",
    "        if self.image is None:\n",
    "            raise ValueError(f\"无法读取图像文件：{image_path}\")\n",
    "        self.gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)\n",
    "        \n",
    "        # 创建输出目录\n",
    "        self.output_dir = Path(\"detection_results\")\n",
    "        self.output_dir.mkdir(exist_ok=True)\n",
    "        \n",
    "        # 结果存储结构\n",
    "        self.results = {\n",
    "            'rgb': None,\n",
    "            'noise': None,\n",
    "            'frequency': None,\n",
    "            'fusion': None,\n",
    "            'score':None\n",
    "        }\n",
    "\n",
    "    def _safe_savefig(self, filename):\n",
    "        \"\"\"图像保存\"\"\"\n",
    "        save_path = self.output_dir / filename\n",
    "        plt.savefig(str(save_path), bbox_inches='tight')\n",
    "        plt.close()\n",
    "        return save_path\n",
    "\n",
    "    def rgb_cooccurrence_analysis(self):\n",
    "        \"\"\"RGB流分析：灰度共生矩阵\"\"\"\n",
    "        try:\n",
    "            # 计算灰度共生矩阵\n",
    "            glcm = graycomatrix(self.gray, distances=[1], angles=[0], \n",
    "                               levels=256, symmetric=True, normed=True)\n",
    "            \n",
    "            # 可视化\n",
    "            plt.figure(figsize=(10, 6))\n",
    "            plt.subplot(121), plt.imshow(self.gray, cmap='gray'), plt.title('Original Image')\n",
    "            plt.subplot(122), plt.imshow(glcm[:, :, 0, 0], cmap='jet'), plt.title('GLCM Matrix')\n",
    "            rgb_path = self._safe_savefig(\"rgb_stream.png\")\n",
    "            \n",
    "            # 存储结果\n",
    "            self.results['rgb'] = {\n",
    "                'contrast': graycoprops(glcm, 'contrast')[0, 0],\n",
    "                'energy': graycoprops(glcm, 'energy')[0, 0],\n",
    "                'path': rgb_path\n",
    "            }\n",
    "        except Exception as e:\n",
    "            raise RuntimeError(f\"RGB流分析失败: {str(e)}\")\n",
    "\n",
    "    def noise_analysis(self):\n",
    "        \"\"\"噪声流分析：误差水平 + PRNU指纹\"\"\"\n",
    "        try:\n",
    "            # 误差水平分析\n",
    "            error_level = self._calculate_error_level()\n",
    "            \n",
    "            # PRNU分析\n",
    "            prnu_map = self._prnu_analysis()\n",
    "            \n",
    "            # 可视化\n",
    "            plt.figure(figsize=(10, 6))\n",
    "            plt.subplot(121), plt.imshow(error_level, cmap='viridis'), plt.title('Error Level Analysis')\n",
    "            plt.subplot(122), plt.imshow(prnu_map, cmap='jet'), plt.title('PRNU Fingerprint')\n",
    "            noise_path = self._safe_savefig(\"noise_stream.png\")\n",
    "            \n",
    "            self.results['noise'] = {\n",
    "                'error_level': np.mean(error_level),\n",
    "                'prnu_score': np.std(prnu_map),\n",
    "                'path': noise_path\n",
    "            }\n",
    "        except Exception as e:\n",
    "            raise RuntimeError(f\"噪声流分析失败: {str(e)}\")\n",
    "\n",
    "    def _calculate_error_level(self):\n",
    "        \"\"\"计算误差水平图\"\"\"\n",
    "        bs = 64  # 块大小\n",
    "        error_map = np.zeros_like(self.gray, dtype=np.float32)\n",
    "        \n",
    "        for i in range(0, self.gray.shape[0], bs):\n",
    "            for j in range(0, self.gray.shape[1], bs):\n",
    "                block = self.gray[i:i+bs, j:j+bs]\n",
    "                if block.shape == (bs, bs):\n",
    "                    error_map[i:i+bs, j:j+bs] = np.std(block)\n",
    "        \n",
    "        return cv2.resize(error_map, self.gray.shape[::-1])\n",
    "\n",
    "    def _prnu_analysis(self):\n",
    "        \"\"\"PRNU指纹提取\"\"\"\n",
    "        denoised = cv2.fastNlMeansDenoising(self.gray, h=3)\n",
    "        prnu = self.gray.astype(np.float32) - denoised.astype(np.float32)\n",
    "        return cv2.normalize(prnu, None, 0, 255, cv2.NORM_MINMAX)\n",
    "\n",
    "    def frequency_analysis(self):\n",
    "        \"\"\"频率流分析：DCT + 小波 + FFT\"\"\"\n",
    "        try:\n",
    "            # DCT变换\n",
    "            dct_coeff = dct(dct(self.gray.T, norm='ortho').T, norm='ortho')\n",
    "            \n",
    "            # 小波变换\n",
    "            _, (cH, _, _) = dwt2(self.gray, 'haar')\n",
    "            \n",
    "            # 傅里叶变换\n",
    "            fft = fftshift(fft2(self.gray))\n",
    "            magnitude = np.log(np.abs(fft) + 1e-10)\n",
    "            \n",
    "            # 可视化\n",
    "            plt.figure(figsize=(15, 6))\n",
    "            plt.subplot(131), plt.imshow(dct_coeff, cmap='jet'), plt.title('DCT Coefficients')\n",
    "            plt.subplot(132), plt.imshow(cH, cmap='jet'), plt.title('Wavelet Coefficients')\n",
    "            plt.subplot(133), plt.imshow(magnitude, cmap='jet'), plt.title('FFT Spectrum')\n",
    "            freq_path = self._safe_savefig(\"frequency_stream.png\")\n",
    "            \n",
    "            self.results['frequency'] = {\n",
    "                'dct_energy': np.mean(dct_coeff**2),\n",
    "                'wavelet_var': np.var(cH),\n",
    "                'fft_anomaly': np.max(magnitude),\n",
    "                'path': freq_path\n",
    "            }\n",
    "        except Exception as e:\n",
    "            raise RuntimeError(f\"频率流分析失败: {str(e)}\")\n",
    "\n",
    "    def fusion_detection(self):\n",
    "        \"\"\"多流特征融合检测\"\"\"\n",
    "        # 检查前置分析\n",
    "        for stream in ['rgb', 'noise', 'frequency']:\n",
    "            if self.results[stream] is None:\n",
    "                raise ValueError(f\"{stream}流分析未完成\")\n",
    "        \n",
    "        # 特征提取\n",
    "        features = {\n",
    "            'rgb_contrast': self.results['rgb']['contrast'],\n",
    "            'noise_error': self.results['noise']['error_level'],\n",
    "            'fft_anomaly': self.results['frequency']['fft_anomaly']\n",
    "        }\n",
    "        \n",
    "        # 动态权重（示例值需根据实际数据调整）\n",
    "        weights = {\n",
    "            'rgb_contrast': 0.3,\n",
    "            'noise_error': 0.4,\n",
    "            'fft_anomaly': 0.3\n",
    "        }\n",
    "        \n",
    "        # 计算综合得分\n",
    "        score = sum(features[k] * weights[k] for k in features)\n",
    "        self.results['score'] = score\n",
    "        \n",
    "        self.results['fusion'] = score > 75  # 可调阈值，得分越高，越不可信\n",
    "        \n",
    "        return self.results['fusion']\n",
    "\n",
    "    \n",
    "    def visualize_results(self):\n",
    "        \"\"\"综合可视化报告\"\"\"\n",
    "        try:\n",
    "            # 执行完整分析流程\n",
    "            analysis_sequence = [\n",
    "                self.rgb_cooccurrence_analysis,\n",
    "                self.noise_analysis,\n",
    "                self.frequency_analysis,\n",
    "                self.fusion_detection\n",
    "            ]\n",
    "            \n",
    "            for func in analysis_sequence:\n",
    "                func()\n",
    "            \n",
    "            # 创建可视化面板\n",
    "            plt.figure(figsize=(18, 12))\n",
    "            \n",
    "            # 原始图像\n",
    "            plt.subplot(2, 3, 1)\n",
    "            plt.imshow(cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB))\n",
    "            plt.title('Original Image')\n",
    "            plt.axis('off')\n",
    "            \n",
    "            # 各分析流结果\n",
    "            streams = [\n",
    "                ('RGB Stream', self.results['rgb']['path']),\n",
    "                ('Noise Stream', self.results['noise']['path']),\n",
    "                ('Frequency Stream', self.results['frequency']['path'])\n",
    "            ]\n",
    "            \n",
    "            for idx, (title, path) in enumerate(streams, start=2):\n",
    "                plt.subplot(2, 3, idx)\n",
    "                img = plt.imread(str(path))\n",
    "                plt.imshow(img)\n",
    "                plt.title(title)\n",
    "                plt.axis('off')\n",
    "            \n",
    "            # 检测结果展示\n",
    "            result = \"Forged\" if self.results['fusion'] else \"Authentic\"\n",
    "            plt.subplot(2, 3, 6)\n",
    "            plt.text(0.5, 0.5, f'Final Result: {result}\\nConfidence: {self._get_confidence():.1%}',\n",
    "                    ha='center', va='center', fontsize=16, \n",
    "                    bbox=dict(facecolor='orange', alpha=0.5))\n",
    "            plt.text(1, 0.5, f'Final Result: {result}\\nSCore: {self.results['score']:.2f}',\n",
    "                    ha='center', va='center', fontsize=16, \n",
    "                    bbox=dict(facecolor='orange', alpha=0.5))\n",
    "            \n",
    "            plt.axis('off')\n",
    "            \n",
    "            # 保存最终结果\n",
    "            final_path = self.output_dir / \"final_report.png\"\n",
    "            plt.tight_layout()\n",
    "            plt.savefig(str(final_path))\n",
    "            plt.close()\n",
    "            \n",
    "            print(f\"分析完成！结果报告已保存至：{final_path}\")\n",
    "            \n",
    "        except Exception as e:\n",
    "            print(f\"可视化过程中发生错误: {str(e)}\")\n",
    "\n",
    "    def _get_confidence(self):\n",
    "        \"\"\"计算置信度（示例逻辑）\"\"\"\n",
    "        features = [\n",
    "            self.results['rgb']['contrast'],\n",
    "            self.results['noise']['error_level'],\n",
    "            self.results['frequency']['fft_anomaly']\n",
    "        ]\n",
    "        return np.mean([f/max(f,1e-5) for f in features])  # 防止除以零\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "ee296252-a90a-46eb-988e-c76f6af6b8c7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "分析完成！结果报告已保存至：detection_results\\final_report.png\n"
     ]
    }
   ],
   "source": [
    "if __name__ == \"__main__\":\n",
    "    detector = MultiStreamForgeryDetection('Image/test_fake.jpeg')\n",
    "    detector.visualize_results()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
